{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Jupyter笔记本中的交互性问题\n",
    "\n",
    "这个笔记本演示了Jupyter中使用widgets时的常见问题：\n",
    "1. 需要手动设置回调函数\n",
    "2. 需要手动重新运行单元格\n",
    "3. 复杂的观察者模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ipywidgets as widgets\n",
    "from IPython.display import display, clear_output\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# 设置 matplotlib 使用支持中文的字体\n",
    "# 'Microsoft YaHei' 是 Windows 系统中常见的字体\n",
    "# 如果您使用 macOS，可以尝试 'PingFang SC'\n",
    "# 如果您使用 Linux，可以尝试 'WenQuanYi Micro Hei'\n",
    "plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] \n",
    "\n",
    "# 解决负号 '-' 显示为方块的问题\n",
    "plt.rcParams['axes.unicode_minus'] = False "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 问题1：需要手动设置回调"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建滑块\n",
    "slider = widgets.IntSlider(\n",
    "    value=50,\n",
    "    min=0,\n",
    "    max=100,\n",
    "    step=1,\n",
    "    description='值:'\n",
    ")\n",
    "\n",
    "# 创建输出区域\n",
    "output = widgets.Output()\n",
    "\n",
    "def on_slider_change(change):\n",
    "    with output:\n",
    "        clear_output(wait=True)\n",
    "        print(f\"当前值: {change['new']}\")\n",
    "        print(f\"平方: {change['new']**2}\")\n",
    "\n",
    "# 手动绑定回调函数\n",
    "slider.observe(on_slider_change, names='value')\n",
    "\n",
    "display(slider, output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题**：\n",
    "- 需要手动创建回调函数\n",
    "- 需要手动管理输出区域\n",
    "- 代码复杂且容易出错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 问题2：多个widgets之间的同步困难"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建多个相关的widgets\n",
    "x_slider = widgets.FloatSlider(value=1.0, min=0.1, max=5.0, step=0.1, description='X:')\n",
    "y_slider = widgets.FloatSlider(value=1.0, min=0.1, max=5.0, step=0.1, description='Y:')\n",
    "operation = widgets.Dropdown(\n",
    "    options=['加法', '乘法', '幂运算'],\n",
    "    value='加法',\n",
    "    description='操作:'\n",
    ")\n",
    "\n",
    "result_output = widgets.Output()\n",
    "plot_output = widgets.Output()\n",
    "\n",
    "def update_calculation(change=None):\n",
    "    x_val = x_slider.value\n",
    "    y_val = y_slider.value\n",
    "    op = operation.value\n",
    "    \n",
    "    if op == '加法':\n",
    "        result = x_val + y_val\n",
    "    elif op == '乘法':\n",
    "        result = x_val * y_val\n",
    "    else:  # 幂运算\n",
    "        result = x_val ** y_val\n",
    "    \n",
    "    with result_output:\n",
    "        clear_output(wait=True)\n",
    "        print(f\"{x_val} {op} {y_val} = {result:.2f}\")\n",
    "    \n",
    "    # 更新图表\n",
    "    with plot_output:\n",
    "        clear_output(wait=True)\n",
    "        fig, ax = plt.subplots(figsize=(6, 4))\n",
    "        x_range = np.linspace(0.1, 5, 100)\n",
    "        if op == '加法':\n",
    "            y_range = x_range + y_val\n",
    "        elif op == '乘法':\n",
    "            y_range = x_range * y_val\n",
    "        else:\n",
    "            y_range = x_range ** y_val\n",
    "        \n",
    "        ax.plot(x_range, y_range)\n",
    "        ax.axvline(x_val, color='red', linestyle='--', alpha=0.7)\n",
    "        ax.set_xlabel('X')\n",
    "        ax.set_ylabel('结果')\n",
    "        ax.set_title(f'函数: f(x) = x {op} {y_val}')\n",
    "        plt.show()\n",
    "\n",
    "# 需要为每个widget手动绑定回调\n",
    "x_slider.observe(update_calculation, names='value')\n",
    "y_slider.observe(update_calculation, names='value')\n",
    "operation.observe(update_calculation, names='value')\n",
    "\n",
    "# 初始计算\n",
    "update_calculation()\n",
    "\n",
    "display(x_slider, y_slider, operation, result_output, plot_output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题**：\n",
    "- 需要为每个widget手动绑定相同的回调函数\n",
    "- 代码重复且难以维护\n",
    "- 容易忘记绑定某个widget\n",
    "- 输出管理复杂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 问题3：状态管理困难"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 全局状态变量\n",
    "current_data = None\n",
    "\n",
    "# 数据生成控件\n",
    "data_size = widgets.IntSlider(value=100, min=10, max=1000, description='数据量:')\n",
    "noise_level = widgets.FloatSlider(value=0.1, min=0, max=1, description='噪声:')\n",
    "generate_button = widgets.Button(description='生成数据')\n",
    "\n",
    "# 分析控件\n",
    "analysis_type = widgets.Dropdown(\n",
    "    options=['均值', '标准差', '最大值', '最小值'],\n",
    "    description='分析:'\n",
    ")\n",
    "\n",
    "data_output = widgets.Output()\n",
    "analysis_output = widgets.Output()\n",
    "\n",
    "def generate_data(button):\n",
    "    global current_data\n",
    "    size = data_size.value\n",
    "    noise = noise_level.value\n",
    "    \n",
    "    # 生成数据\n",
    "    current_data = np.random.normal(0, 1, size) + np.random.normal(0, noise, size)\n",
    "    \n",
    "    with data_output:\n",
    "        clear_output(wait=True)\n",
    "        print(f\"已生成 {size} 个数据点，噪声水平: {noise}\")\n",
    "        plt.figure(figsize=(8, 4))\n",
    "        plt.hist(current_data, bins=30, alpha=0.7)\n",
    "        plt.title('数据分布')\n",
    "        plt.show()\n",
    "    \n",
    "    # 触发分析更新\n",
    "    update_analysis()\n",
    "\n",
    "def update_analysis(change=None):\n",
    "    global current_data\n",
    "    if current_data is None:\n",
    "        with analysis_output:\n",
    "            clear_output(wait=True)\n",
    "            print(\"请先生成数据\")\n",
    "        return\n",
    "    \n",
    "    analysis = analysis_type.value\n",
    "    \n",
    "    if analysis == '均值':\n",
    "        result = np.mean(current_data)\n",
    "    elif analysis == '标准差':\n",
    "        result = np.std(current_data)\n",
    "    elif analysis == '最大值':\n",
    "        result = np.max(current_data)\n",
    "    else:  # 最小值\n",
    "        result = np.min(current_data)\n",
    "    \n",
    "    with analysis_output:\n",
    "        clear_output(wait=True)\n",
    "        print(f\"{analysis}: {result:.4f}\")\n",
    "\n",
    "generate_button.on_click(generate_data)\n",
    "analysis_type.observe(update_analysis, names='value')\n",
    "\n",
    "display(data_size, noise_level, generate_button, data_output)\n",
    "display(analysis_type, analysis_output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题**：\n",
    "- 需要使用全局变量管理状态\n",
    "- 手动管理widget之间的依赖关系\n",
    "- 容易出现状态不一致的问题\n",
    "- 代码结构复杂，难以理解和维护"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "marimo-ui (3.12.11)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
